package org.goldenport.g3

import scala.collection.mutable.ArrayBuffer
import scala.xml.Node
import java.io._
import org.goldenport.g3.atom._
import org.goldenport.g3.messages._
import org.goldenport.g3.events._

/**
 * @since   Aug.  5, 2010
 * @version Oct.  5, 2010
 * @author  ASAMI, Tomoharu
 */
abstract class G3Driver extends G3Contextable {
  final def open(ctx: G3Context, params: AnyRef*) {
    setContext(ctx)
    open_Driver(params.toList)
  }

  protected def open_Driver(params: List[AnyRef]) {
  }

  final def close() {
    close_Driver()
  }

  protected def close_Driver() {
  }

  def createDriverActivity(ctx: G3AgentContext): PartialFunction[AnyRef, AnyRef] = {
    rest_activity(ctx) orElse
    driver_Activity(ctx)
  }

  private def rest_activity(ctx: G3AgentContext): PartialFunction[AnyRef, AnyRef] = {
    case get: Get => execute(get, ctx)
    case post: Post => execute(post, ctx)
    case put: Put => execute(put, ctx)
    case delete: Delete => execute(delete, ctx)
  }

  protected def driver_Activity(ctx: G3AgentContext): PartialFunction[AnyRef, AnyRef] = {
    scala.collection.immutable.Map[AnyRef, AnyRef]()
  }

  protected def new_Agent(cmd: G3Command, context: G3AgentContext): G3DriverAgent

  def execute(cmd: G3Command, context: G3AgentContext): G3Event = {
    val agent = new_Agent(cmd, context)
    agent.open()
    try {
      val evt = agent.execute(cmd)
      evt.inputMessage = Some(cmd)
      evt
    } catch {
      case e: Exception => {
        new CatchExceptioned(cmd.uri, e) {
          inputMessage = Some(cmd)
        }
      }
    } finally {
      agent.close()
    }
  }
/*
  def execute(uri: String, msg: G3Message, context: G3AgentContext): G3Event = {
    msg match {
      case cmd: Get => get(uri, cmd, context)
      case cmd: Post => post(uri, cmd, context)
      case cmd: Put => put(uri, cmd, context)
      case cmd: Delete => delete(uri, cmd, context)
      case cmd: Mkcol => mkcol(uri, cmd, context)
    } // XXX orElse
  }

  def get(uri: String, msg: Get, context: G3AgentContext): Getted = {
    val agent = new_Agent(uri, context)
    agent.open()
    try {
      agent.get(msg) match {
        case evt: Getted => {
          evt.input = Some(msg.content)
          evt.inputMessage = Some(msg)
          evt
        }
        case output => new Getted(uri) {
          input = Some(msg.content)
          output = make_output(output)
          inputMessage = Some(msg)
        }
      }
    } finally {
      agent.close()
    }
  }

  def post(uri: String, msg: Post, context: G3AgentContext): Posted = {
    val agent = new_Agent(uri, context)
    agent.open()
    try {
      agent.post(msg) match {
        case evt: Posted => {
          evt.input = Some(msg.content)
          evt.inputMessage = Some(msg)
          evt
        }
        case output => new Posted(uri) {
          input = Some(msg.content)
          output = make_outpu(toutput)
          inputMessage = Some(msg)
        }
      }
    } finally {
      agent.close()
    }
  }

  def put(uri: String, msg: Put, context: G3AgentContext): Putted = {
    val agent = new_Agent(uri, context)
    agent.open()
    try {
      agent.put(msg) match {
        case evt: Putted => {
          evt.input = Some(msg.content)
          evt.inputMessage = Some(msg)
          evt
        }
        case output => new Putted(uri) {
          input = Some(msg.content)
          output = make_output(output)
          inputMessage = Some(msg)
        }
      }
    } finally {
      agent.close()
    }
  }

  def delete(uri: String, msg: Delete, context: G3AgentContext): Deleted = {
    val agent = new_Agent(uri, context)
    agent.open()
    try {
      agent.delete(msg) match {
        case evt: Deleted => {
          evt.inputMessage = Some(msg)
          evt
        }
        case output => new Deleted(uri) {
//          input = Some(msg.content)
          output = make_output(output)
          inputMessage = Some(msg)
        }
      }
    } finally {
      agent.close()
    }
  }

  def mkcol(uri: String, msg: Mkcol, context: G3AgentContext): Mkcoled = {
    val agent = new_Agent(uri, context)
    agent.open()
    try {
      agent.mkcol(msg) match {
        case evt: Mkcoled => {
          evt.inputMessage = Some(msg)
          evt
        }
        case output => new Mkcoled(uri) {
//          input = Some(msg.content)
          output = make_output(output)
          inputMessage = Some(msg)
        }
      }
    } finally {
      agent.close()
    }
  }

  protected final def make_output(value: Any) = {
    value match {
      case None => None
      case u: Unit => None
      case _ => Some(value)
    }
  }
*/
}

abstract class G3DriverAgent(val uri: String, val context: G3AgentContext) {
  private val _funcs = new ArrayBuffer[PartialFunction[G3Command, G3Event]]

  protected final def agent(func: PartialFunction[G3Command, G3Event]) {
    _funcs += func
  }

  agent {
    case cmd: Get => get(cmd)
    case cmd: Post => post(cmd)
    case cmd: Put => put(cmd)
    case cmd: Delete => delete(cmd)
    case cmd: Mkcol => mkcol(cmd)
  }

  def open() {
  }

  def close() {
  }

  def execute(cmd: G3Command): G3Event = {
    _funcs.find(_.isDefinedAt(cmd)) match {
      case Some(func) => func(cmd)
      case None => NoEvent(uri, cmd)
    }
  }

  def get(msg: Get): G3Event
  def post(msg: Post): G3Event
  def put(msg: Put): G3Event
  def delete(msg: Delete): G3Event
  def mkcol(msg: Mkcol): G3Event
}
